/* ------------------------------------------------------------------------*/
/*       arc20_init                 initialize the hardware                */
/*       arc20_read                 read a packet                          */
/*       arc20_write                write a packet                         */
/*       arc20_status               status of node                         */
/*       arc20_exit                 reset and cleanup                      */
/* ------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/mman.h>
#include <hw/inout.h>
#include "arcdef.h"
#include "arc20.h"

/* recv,tx page defines */
#define ENABLE_PAGE0   0x84
#define ENABLE_PAGE1   0x8c

static  uint8_t count [2];


/****************************************************************************
 * FUNCTION NAME: arc20_init
 *
 * DESCRIPTION  : initial the node and join the network
 *
 * RETURN VALUE : status
 ***************************************************************************/
int arc20_init (arc20_t *arc20, int base_addr, int lvl_intr)
{
    int status;
    uint8_t cmd;
    uint8_t config;
	uint8_t buffer [512];

	arc20->port = mmap_device_io (10, base_addr);
	arc20->swport = mmap_device_io (1, base_addr+9);
	arc20->lvl_intr = lvl_intr;

    printf ("Initializing 20020 at %XH, , Interrupt Level %d\n", arc20->port, arc20->lvl_intr);

    /* an 8 bit interface uses +1x, a 16 bit interface users +2x */
    arc20->interrupt_reg 	= arc20->port;
    arc20->status_reg 		= arc20->port;
    arc20->command_reg 		= arc20->port + 1;
    arc20->diag_reg 		= arc20->port + 1;
    arc20->addr_hi_reg 		= arc20->port + 2;
    arc20->addr_low_reg 	= arc20->port + 3;
    arc20->packet_mem 		= arc20->port + 4;
    arc20->config_reg 		= arc20->port + 6;
    arc20->tentid_reg 		= arc20->port + 7;
    arc20->nextid_reg 		= arc20->port + 7;
    arc20->nodeid_reg 		= arc20->port + 7;
    arc20->setup_reg 		= arc20->port + 7;

    /* set the page numbers */
    arc20->in_page = 0;
    arc20->out_page = 2;
    arc20->rx_flag = FALSE;

    /* setup to use node id register */
    config = in8 (arc20->config_reg);
    config &= SUB_AD;
    config |= NODE_ID_REG;
    out8 (arc20->config_reg, config);

    /* clear the POR and RECON status bits */
    cmd = CLEARFLAGS;
    out8 (arc20->command_reg, cmd);

    /* determine the node id - read DIP switch */
    arc20->nodeid = in8 (arc20->swport);
    if (arc20->nodeid == 0) printf ("Invalid Nodeid\n");
	else printf ("Nodeid = %d\n", arc20->nodeid);
    out8 (arc20->nodeid_reg, arc20->nodeid);    
    delay (200);    

    /* reset the 20020 chip - toggle it */
    config = in8 (arc20->config_reg);
    config |= RESET;
    out8 (arc20->config_reg, config);
    config &= ~RESET;
    out8 (arc20->config_reg, config);
    delay (200);    

    /* get the reset indicator it must be a 0xd1 */
    out8 (arc20->addr_hi_reg, 0xc0);
    out8 (arc20->addr_low_reg, 0);
    buffer[0] = in8 (arc20->packet_mem);
    buffer[1] = in8 (arc20->packet_mem);
    if (buffer[0] != 0xD1) status = 0;
    else {
        /* mask interrupts */        
        out8 (arc20->interrupt_reg,0);

        /* disable tx,rx */
        out8 (arc20->command_reg, DISABLETX);
        out8 (arc20->command_reg, DISABLERX);

        /* clear the POR and RECON status bits */
        cmd = CLEARFLAGS;
        out8 (arc20->command_reg, cmd);

        /* short or long packets */
        out8 (arc20->command_reg, LONGPACKET);

        /* setup network speed - 5Mbps */
        config &= SUB_AD;		// стереть все биты кроме подадреса
        config |= SETUP_REG;	// выбрать setup1
        out8 (arc20->config_reg, config);
        out8 (arc20->setup_reg, 0);

        /* interrupt driven */
        if (arc20->use_intr == TRUE) 
        {
//			InterruptLock ();
            /* enable recv, recon, exenak */
//			out8 (arc20->interrupt_reg, 0x8c);
//			InterruptUnlock ();
        }

        /* configuration - network timeout slowest, enable tx */
        config = US78 | TXEN | CCHEN;

        /* output the configuration - join the network */
        out8 (arc20->config_reg, config);

        /* enable two reads, w/broadcast */
        cmd = ENABLE_PAGE0;
        out8 (arc20->command_reg, cmd);
        cmd = ENABLE_PAGE1;
        out8 (arc20->command_reg, cmd);

        /* return the node ID */
        arc20->nodeid = buffer [1];
        status = arc20->nodeid;
    }

    /* return 0 if bad initialization, or node ID */
    return (status);
}

/****************************************************************************
 * FUNCTION NAME: arc20_read
 *
 * DESCRIPTION  : read a packet
 *
 * RETURN VALUE : status
 ***************************************************************************/
int arc20_read (arc20_t *arc20, uint8_t *data_ptr)
{
	int status;
    uint16_t bufcount,i;
    uint8_t cmd;

    /* check if interrupt receive */
//    if (arc20_intr == TRUE)
//    {
        /* check if buffers have been filled */
//        if (arc20_rx_flag == TRUE)
//        {
            /* get the buffer count */
//            bufcount = arc20_buffer[3] + 4;
//            if (arc20_buffer[2] > 0) bufcount += 256;

            /* transfer the buffer to user */
//            for (i=0; i<bufcount; i++)
//            {
//                *(data_ptr+i) = arc20_buffer[i];
//            }         
            
//            arc20_rx_flag = FALSE;
//            status = E_OK;
//        }
//        else
//        {
            /* no packets */
//            status = E_NO_PACKET;
//        }
//    }
//    else
//    {
        /* check the recieve (RI) bit */
        arc20_get_check_status (arc20);
        status = arc20->stat;
        if ((status & RI) == RI) 
        {
            /* reset command chaining */
            out8 (arc20->command_reg, 0x08);

            /* enable the next read page */
            arc20->page = arc20->in_page;
            arc20->in_page++;
            arc20->in_page &= 1;
            status = E_OK;

            /* read the 1st 2 bytes sid,did */
            if (arc20->page == 0)
            {
                out8 (arc20->addr_hi_reg,0xc0);
            }
            else
            {
                out8 (arc20->addr_hi_reg,0xc2);
            }
            out8 (arc20->addr_low_reg,0);
            *data_ptr = in8 (arc20->packet_mem);
            *(data_ptr+1) = in8 (arc20->packet_mem);

            /* read the packet size */
            count [0] = in8 (arc20->packet_mem);
            count [1] = in8 (arc20->packet_mem);

            /* store real count */

            /* long or short */
            if (count[0] == 0) 
            {
                arc20->offset = count [1];
                arc20->nbytes = (uint8_t)((~count [1])+1);
                *(data_ptr+3) = arc20->nbytes;
                *(data_ptr+2) = 1;
                bufcount = 256 + arc20->nbytes;
            }
            else
            {
                arc20->offset = count [0];
                arc20->nbytes = (uint8_t)((~count [0])+1);
                *(data_ptr+3) = arc20->nbytes;
                *(data_ptr+2) = 0;
                bufcount = arc20->nbytes;
            }

            /* read the next bytes */
            if (arc20->page == 0)
            {
                out8 (arc20->addr_hi_reg, 0xc0);
            }
            else
            {
                out8 (arc20->addr_hi_reg, 0xc2);
            }
            out8 (arc20->addr_low_reg, arc20->offset);
            
            /* transfer the buffer to storage */
            for (i=0; i<bufcount; i++)
            {
                *(data_ptr+4+i) = in8 (arc20->packet_mem);
            }

            /* enable this page */
            if (arc20->in_page == 0) cmd = ENABLE_PAGE1;
            else cmd = ENABLE_PAGE0;
            out8 (arc20->command_reg, cmd);
        }
        else
        {
            status = E_NO_PACKET;
        }
//    }

    /* return system status */
    return(status);
}

/****************************************************************************
 * FUNCTION NAME: arc20_write
 *
 * DESCRIPTION  : write a packet
 *
 * RETURN VALUE : status
 ***************************************************************************/
int arc20_write (arc20_t *arc20, uint8_t *data_ptr)
{
    uint8_t page;
    uint8_t count;
    uint8_t lors;
    uint8_t offset;
//    uint8_t cmd;
    uint8_t counts [2];
    int status;

    /* check if available */
    arc20_get_check_status (arc20);
    status = arc20->stat;
    if ((status & TA2) > 0) 
    {
        status = E_OK;
        page = arc20->out_page;

        /* SID and DID */
        arc20_write_data (arc20, page, 0, 2, 0, data_ptr);

        /* user packet format - SID, DID, msbcount, lsbcount, data,... */
        data_ptr += 2;

        /* determine the count and packet size to use */
        if (*data_ptr > 0) {

            /* long packet */
            lors = 1;
            data_ptr++;
            count = *data_ptr;
            if (count == 0) status = E_BAD_PACKET_SIZE;
            else {
                offset = (uint8_t)((~count)+1);
                counts [0] = 0;
                counts [1] = offset;
            }
        }
        else {
            /* short packet */
            lors = 0;
            data_ptr++;
            count = *data_ptr;
            if (count > 253) status = E_BAD_PACKET_SIZE;
            else {
                offset = (uint8_t)((~count)+1);
                counts [0] = offset;
            }
        } 

        /* load the buffer */
        if (status == E_OK) {

            /* load the count/fp */
            arc20_write_data (arc20, page, 2, 2, 0, &counts [0]);

            /* load the data */
            data_ptr++;
            arc20_write_data (arc20, page, offset, count, lors, data_ptr);

            /* reset ack flag and abort flag */
            arc20->write_ack = FALSE;
            arc20->write_abort = FALSE;

            /* send it out */
            page <<= 3;
            page |= 3;
            out8 (arc20->command_reg, page);

            /* incr page and check for rollover */
            arc20->out_page++;
            if (arc20->out_page > 3) arc20->out_page = 2;
        }
    }
    else status = E_TX_BUSY;

    /* return system status */
    return (status);
}

/****************************************************************************
 * FUNCTION NAME: arc20_status
 *
 * DESCRIPTION  : status of network
 *
 * RETURN VALUE : status
 ***************************************************************************/
uint8_t arc20_status (arc20_t *arc20)
{
    /* get the status */
    arc20_get_check_status (arc20);

    /* return status */
    return (arc20->stat);
}


/****************************************************************************
 * FUNCTION NAME: arc20_interrupt
 *
 * DESCRIPTION  : interrupt routine
 *
 * RETURN VALUE : none
 ***************************************************************************/
//void interrupt far arc20_interrupt(void)
//{
//    uint16_t i,bytes;
//    uint8_t addr_low_save, addr_high_save;

    /* get the status */
//    arc20_stat = (uint8_t)INP(arc20_status_reg);
//    arc20_diag = (uint8_t)INP(arc20_diag_reg);

    /* receiver */
//    if ((arc20_stat & RI) == RI)
//    {
        /* set the flag */
//        arc20_rx_flag = TRUE;

        /* reset command chaining */
//        OUTP(arc20_command_reg,0x08);

        /* get the current page */
//        arc20_page = arc20_in_page;
//        arc20_in_page++;
//        arc20_in_page &= 1;

        /* save the address_low register and the address_high register */
        /* before modification */
//        addr_low_save = INP(arc20_addr_low_reg);
//        addr_high_save  = INP(arc20_addr_hi_reg);

        /* read the 1st 2 bytes sid,did */
//        if (arc20_page == 0)
//        {
//            OUTP(arc20_addr_hi_reg,0xc0);
//        }
//        else
//        {
//            OUTP(arc20_addr_hi_reg,0xc2);
//        }
//        OUTP(arc20_addr_low_reg,0);
//        arc20_buffer[0] = (uint8_t)INP(arc20_packet_mem);
//        arc20_buffer[1] = (uint8_t)INP(arc20_packet_mem);

        /* read the packet size */
//        count[0] = (uint8_t)INP(arc20_packet_mem);
//        count[1] = (uint8_t)INP(arc20_packet_mem);

        /* store real count */
        /* long or short */
//        if (count[0] == 0) 
//        {
//            arc20_offset = count[1];
//            arc20_nbytes = (uint8_t)((~count[1])+1);
//            arc20_buffer[3] = arc20_nbytes;
//            arc20_buffer[2] = 1;
//            bytes = 256 + arc20_nbytes;
//        }
//        else
//        {
//            arc20_offset = count[0];
//            arc20_nbytes = (uint8_t)((~count[0])+1);
//            arc20_buffer[3] = arc20_nbytes;
//            arc20_buffer[2] = 0;
//            bytes = arc20_nbytes;
//        }

        /* read the next bytes */
//        if (arc20_page == 0)
//        {
//            OUTP(arc20_addr_hi_reg,0xc0);
//        }
//        else
//        {
//            OUTP(arc20_addr_hi_reg,0xc2);
//        }
//        OUTP(arc20_addr_low_reg,arc20_offset);
            
        /* transfer the buffer to storage */
//        for (i=0; i<bytes; i++)
//        {
//            arc20_buffer[4+i] = (uint8_t)INP(arc20_packet_mem);
//        }

        /* enable this page */
//        if (arc20_in_page == 0) arc20_cmd = ENABLE_PAGE1;
//        else arc20_cmd = ENABLE_PAGE0;
//        OUTP(arc20_command_reg,arc20_cmd);

        /* restore original address_high and address_low registers */
//        OUTP(arc20_addr_hi_reg, addr_high_save);
//        OUTP(arc20_addr_low_reg, addr_low_save);
//    }

    /* recon */
//    if ((arc20_stat & RECON) > 0)
//    {
//        OUTP(arc20_command_reg,0x1e);
//    }

    /* execessive nak */
//    if ((arc20_diag & EXC_NAK) > 0)
//    {
        /* let the write status routine abort the packet */
//        OUTP(arc20_command_reg,0x1e);
//        OUTP(arc20_command_reg,0x01);
//        arc20_write_abort = TRUE;
//    }

    /* send eoi */
//    OUTP(0x20, 0x20);
//}


/****************************************************************************
 * FUNCTION NAME: arc20_write_data
 *
 * DESCRIPTION  : Write data from buffer location to a specific page
 *
 * RETURN VALUE : none
 ***************************************************************************/
void arc20_write_data (arc20_t *arc20, uint8_t page, uint8_t offset, uint8_t count, 
						uint8_t shortlong, uint8_t *user_buffer)
{
    uint16_t address;

    /* set the address pointers, autoincrement on write */
    address = (512 * page) + offset;
    out8 (arc20->addr_hi_reg, (uint8_t)((address >> 8) | AUTOINC) & WRDATA);
    out8 (arc20->addr_low_reg, (uint8_t)address);

    /* write the bytes to the page */
    while (count--) 
    {
        out8 (arc20->packet_mem, *user_buffer++);
    }

    /* do it another 256x */
    if (shortlong == 1) {
        count = 0;
        do {
            out8 (arc20->packet_mem, *user_buffer);
            user_buffer++;
            count--;
        } while (count != 0);
    }
}


/****************************************************************************
 * FUNCTION NAME: arc20_get_check_status
 *
 * DESCRIPTION  : get and check the device status
 *
 * RETURN VALUE : none
 ***************************************************************************/
void arc20_get_check_status (arc20_t *arc20)
{
    /* read the status */
    arc20->stat = in8 (arc20->status_reg);
    arc20->diag = in8 (arc20->diag_reg);

    /* check for TMA ack */
    if ((arc20->stat & TMA) == TMA) {
        arc20->write_ack = TRUE; 
    }

    /* check for MYRECON */
    if ((arc20->diag & MYRECON) == MYRECON) {

        /* clear flags */
        out8 (arc20->command_reg, CLEARFLAGS);
        arc20->stat = in8 (arc20->status_reg);
    }

    /* check for RECON */
    if ((arc20->stat & RECON) == RECON) {

        /* clear flags */
        out8 (arc20->command_reg, CLEARFLAGS);
        arc20->stat = in8 (arc20->status_reg);
    }

    /* check for POR flag */
    if ((arc20->stat & POR) == POR) {

        /* clear flags */
        out8 (arc20->command_reg, CLEARPOR);
    }

    /* check for EXENAK */
    if ((arc20->diag & EXC_NAK) == EXC_NAK) {

        /* clear flags */
        out8 (arc20->command_reg, CLEARPOR);

        /* kill the last transmit */
        out8 (arc20->command_reg, DISABLETX);

        /* notify the write routine */
        arc20->write_abort = TRUE;
    }

    /* read the status again */
    arc20->stat = in8 (arc20->status_reg);
    arc20->diag = in8 (arc20->diag_reg);
}

/****************************************************************************
 * FUNCTION NAME: arc20_translate_status
 *
 * DESCRIPTION  : describes status and places report to string
 *
 * RETURN VALUE : none
 ***************************************************************************/
void arc20_translate_status (arc20_t *arc20, char *str)
{
	*str = '\0';
	if ((arc20->stat >>7)& 1) str += sprintf (str, "Receiver Inhibited, ");
	if ((arc20->stat >>4)& 1) str += sprintf (str, "Power On Reset, ");
	if ((arc20->stat >>3)& 1) str += sprintf (str, "Test, ");
	if ((arc20->stat >>2)& 1) str += sprintf (str, "Reconfiguration, ");
	if ((arc20->stat >>1)& 1) str += sprintf (str, "Transmitter Message Ack, ");
	if ((arc20->stat >>0)& 1) str += sprintf (str, "Transmitter Available, ");
}

/****************************************************************************
 * FUNCTION NAME: arc20_exit  
 *
 * DESCRIPTION  : turn off 20020 and disable interrupt if used
 *
 * RETURN VALUE : none
 ***************************************************************************/
void arc20_exit (arc20_t *arc20)
{
	uint8_t config;

    /* disable the transmitter */
    out8 (arc20->command_reg, DISABLETX);

    /* disable the reciever */
    out8 (arc20->command_reg, DISABLERX);

    /* disable the tx enable */
    config = in8 (arc20->config_reg);
    config &= ~TXEN;
    out8 (arc20->config_reg, config);

    /* reset the interrupt vector if set */
//    if (arc20_intr == TRUE) {

        /* exchange vectors */
//        DISABLE;
//        farptr = INT0;
//        for (i=0; i<arc20_level; i++) farptr++;
//        *farptr = *oldvector;
//        mask = (uint8_t)INP(0x21);
//        mask |= (1 << arc20_level);
//        OUTP(0x21, mask);
//        OUTP(arc20_interrupt_reg, 0x00);
//        ENABLE;
//    }

	munmap_device_io (arc20->port, 10);
	munmap_device_io (arc20->swport, 1);
}


/****************************************************************************
 * FUNCTION NAME: net_map
 *
 * DESCRIPTION  : read the network map
 *
 * RETURN VALUE : status
 ***************************************************************************/
void net_map (arc20_t *arc20, uint8_t *data_ptr)
{
    uint8_t	 waittokens;
	uint16_t nodecount;
	uint8_t  nextid;
	uint8_t  tentid;
	uint8_t  config;

    /* read the next ID */
    config = in8 (arc20->config_reg);
    config &= SUB_AD;
    config |= NEXT_ID_REG;
    out8 (arc20->config_reg, config);
    nextid = in8 (arc20->nextid_reg);    
    if (nextid == 0) nextid = arc20->nodeid;

    /* read the current tentative ID */
    config = in8 (arc20->config_reg);
    config &= SUB_AD;
    config |= TENT_ID_REG;
    out8 (arc20->config_reg, config);
    tentid = in8 (arc20->tentid_reg);    

    nodecount = 0;
    while (nodecount <= 255) 
    {
        /* output new address */
        out8 (arc20->tentid_reg, (uint8_t)nodecount);

        waittokens = 255;
        while (waittokens > 0) 
        {
            /* wait 1 token rotation */
            net_tokens (arc20, 1);

            /* look for id - if set then set bit in node map */
            if (((arc20->diag & TENT_ID) == TENT_ID) || 
                ((uint8_t)nodecount == nextid) ||
                ((uint8_t)nodecount == arc20->nodeid)) 
            {
                *(data_ptr+nodecount) = (uint8_t) nodecount;

                 /* time to exit loop */
                 waittokens = 1;
            }
            else *(data_ptr+nodecount) = 0;

            /* token counter */
            waittokens--;
        }
        /* get node */
        nodecount++;
    }
}

/****************************************************************************
 * FUNCTION NAME: net_tokens
 *
 * DESCRIPTION  : wait x token rotations
 *
 * RETURN VALUE : none
 ***************************************************************************/
void net_tokens (arc20_t *arc20, uint8_t ntokens)
{
    uint8_t recon_cnt = 0;

    /* clear dupid seen bit */
    arc20->diag = in8 (arc20->diag_reg);
    arc20->diag = in8 (arc20->diag_reg);

    /* clear RECON flag */
    out8 (arc20->command_reg, 0x1e); 

    /* wait x rotations */
    while (ntokens > 0) 
    {
        /* wait for a token rotation */
        while (((arc20->diag & DUPID) == 0) && (recon_cnt <= 3) && 
               ((arc20->diag & MYRECON) != MYRECON))  
        {
            arc20->stat = in8 (arc20->status_reg);
            if ((arc20->stat & RECON) == RECON) 
            {
               recon_cnt++;
               out8 (arc20->command_reg, 0x1e);
            }
            arc20->diag = in8 (arc20->diag_reg);
        }

        /* clear the DUPID bit */
        arc20->diag = in8 (arc20->diag_reg);
        recon_cnt = 0;

        /* decr the number of tokens */
        ntokens--;
    }
}